﻿using System;

namespace Simp.CodeModel
{
	/// <summary>Encapsulates a scope of many functions resulting into only one.</summary>
	/// <remarks>This function doesn't simplify itself and is meant to be simplified from the outside.</remarks>
	public class ScopeFunction : IFunction, IFunctionSerializer
	{
		private readonly IFunction[] _definitionList;
		private readonly IFunction _returnFunction;

		/// <summary>Constructor of a scope.</summary>
		/// <param name="returnFunction"></param>
		/// <param name="definitionList"></param>
		public ScopeFunction(IFunction returnFunction, params IFunction[] definitionList)
		{
			if (returnFunction == null)
			{
				throw new ArgumentNullException("returnFunction");
			}
			if (definitionList == null)
			{
				throw new ArgumentNullException("definitionList");
			}
			_returnFunction = returnFunction;
			_definitionList = definitionList;
		}

		#region IFunction Members
		string IFunction.FunctionName
		{
			get { return "Scope"; }
		}

		string[] IFunction.ParameterNames
		{
			get { return FunctionHelper.EmptyParameterNames; }
		}

		IFunctionParameterBinding[] IFunction.DefaultParameterValues
		{
			get { return FunctionHelper.EmptyBindings; }
		}

		IFunction IFunction.Simplify(SimplificationMode simplificationMode, params IFunction[] parameters)
		{
			FunctionHelper.ValidateParameters(this, parameters);

			return null;
		}

		IFunction IFunction.AcceptVisitor(IFunctionVisitor visitor)
		{
			bool hasDefinitionsChanged = false;
			IFunction newReturnFunction = _returnFunction.AcceptVisitor(visitor);
			IFunction[] newDefinitionList = new IFunction[_definitionList.Length];

			for (int i = 0; i != newDefinitionList.Length; ++i)
			{
				IFunction currentDefinition = _definitionList[i];
				IFunction newDefinition = visitor.Visit(currentDefinition);

				if (newDefinition != null)
				{
					newDefinitionList[i] = newDefinition;
					hasDefinitionsChanged = true;
				}
				else
				{
					newDefinitionList[i] = _definitionList[i];
				}
			}

			if (hasDefinitionsChanged || newReturnFunction != null)
			{
				return new ScopeFunction(
					newReturnFunction == null ? _returnFunction : newReturnFunction,
					newDefinitionList);
			}
			else
			{	//	Nothing has changed
				return null;
			}
		}
		#endregion

		#region IFunctionSerializer Members
		void IFunctionSerializer.Serialize(AppendOnlyStringBuilder builder, params IFunction[] parameters)
		{
			FunctionHelper.ValidateParameters(this, parameters);

			if (builder == null)
			{
				throw new ArgumentNullException("builder");
			}

			builder.Append("(");
			foreach (IFunction definition in _definitionList)
			{
				FunctionHelper.Serialize(definition);
				builder.Append("   ");
			}
			FunctionHelper.Serialize(_returnFunction);
			builder.Append(")");
		}
		#endregion

		/// <summary>Returns the list of definition function.</summary>
		public IFunction[] DefinitionList
		{
			get { return _definitionList; }
		}

		/// <summary>Returns the function returned by that scope.</summary>
		public IFunction ReturnFunction
		{
			get { return _returnFunction; }
		}
	}
}